-
Notifications
You must be signed in to change notification settings - Fork 47
[feat]: add experimental WorkflowUI telemetry hooks #221
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
import Foundation | ||
import UIKit | ||
|
||
public protocol WorkflowUIEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looking for feedback on the general structuring of the event types and related protocols
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Structure looks good to me
@_spi(WorkflowUIGlobalObservation) import WorkflowUI | ||
import XCTest | ||
|
||
open class WorkflowUIObservationTestCase: XCTestCase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's some scratch work in here, but some ideas for how one could tie into this system and forward events through Combine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me and flows well with the rest of the WorkflowUI code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approach lgtm too, seems easy to use
super.viewDidLayoutSubviews() | ||
defer { super.viewDidLayoutSubviews() } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this change needed for your hooks to work? We were considering moving the frame assignment to viewWillLayoutSubviews
as part of the ViewEnvironmentUI
feature, but wanted to isolate that change due to potential for side effects in code that was depending on this order of events.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no i don't think this change is necessary. the previous implementation of super.viewDidLayoutSubviews()
did nothing (according to the docs), and the new one emits an event. it seems like the event emission should occur after any logic in this method, but i don't think the ordering relative to the frame assignment will matter to any of the envisioned consumers.
if we're potentially going to move the frame assignment out of this method, then perhaps we should leave this proposed change since it will preserve the relative ordering with the new observation events?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me!
WorkflowSwiftUI contains a type WorkflowHostingViewController
that should probably now subclass WorkflowUIViewController
, too.
Other approaches to SwiftUI support that we're experimenting with involve a view controller type that sublclasses UIHostingController
and so cannot subclass WorkflowUIViewController
.
However, I think any view controller type like that can be modified to contain, rather than subclass, UIHostingController
. The only downside AFAIK would be a deeper VC hierarchy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit nervous about the reliance on a UIViewController
superclass when ViewControllerDescription
does not enforce this, but it seems like it may be worth the alternative given our goals!
ViewControllerDescriptions
not describing a ScreenViewController
subclass does seem like a potential footgun here (what @square-tomb pointed out in a previous comment).
Are we planning on adding documentation that describes in detail how to adhere to these event contract rules once it's out of alpha? Could Swift macros help make adding adherence easier in the future without needing to wrap one VC in another? It'd be nice to avoid needing to compose a nested VC to get this behavior.
Agreed. I don't think this is something we can/should enforce here, IMO. |
I wonder, do we need events from If not, and |
@@ -22,7 +22,7 @@ import UIKit | |||
import Workflow | |||
|
|||
/// Drives view controllers from a root Workflow. | |||
public final class WorkflowHostingController<ScreenType, Output>: UIViewController where ScreenType: Screen { | |||
public final class WorkflowHostingController<ScreenType, Output>: WorkflowUIViewController where ScreenType: Screen { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@square-tomb responding here for threading purposes:
WorkflowSwiftUI contains a type
WorkflowHostingViewController
that should probably now subclassWorkflowUIViewController
, too.
good callouts, but given the current motivations for this interface, i think it's okay to punt on the story with SwiftUI and ignore this for the time being.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder, do we need events from
ScreenViewController
at all? Maybe the events fromDescribedViewController
are sufficient if that VC usually maps 1:1 to a screen.
being able to recover the Screen type from ScreenViewController may be of use
If not, and
DescribedViewController
is often used to display a succession of different screens, we would have to add some events here. Or we could wrapcurrentViewController
in a containerWorkflowUIViewController
if we don't mind deepening the VC hierarchy.
adding some additional events that are more specific to the logic in some of the VC subtypes may be something we want to do eventually, but for now i thought we'd just start with VC lifecycle information and see how far that gets us.
@@ -22,7 +22,7 @@ import UIKit | |||
import Workflow | |||
|
|||
/// Drives view controllers from a root Workflow. | |||
public final class WorkflowHostingController<ScreenType, Output>: UIViewController where ScreenType: Screen { | |||
public final class WorkflowHostingController<ScreenType, Output>: WorkflowUIViewController where ScreenType: Screen { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@n8chur responding here for threading purposes:
I'm a bit nervous about the reliance on a
UIViewController
superclass whenViewControllerDescription
does not enforce this, but it seems like it may be worth the alternative given our goals!
that's a reasonable point, but i think we can revisit this if it ends up being problematic in practice.
ViewControllerDescriptions
not describing aScreenViewController
subclass does seem like a potential footgun here (what @square-tomb pointed out in a previous comment).
that's true, though i'm not sure if that will pose an issue for the initial use of this information. @amorde any concerns with this?
Are we planning on adding documentation that describes in detail how to adhere to these event contract rules once it's out of alpha?
yep, this is a first pass so i have low conviction on what exactly the 'right' or desired contract is.
Could Swift macros help make adding adherence easier in the future without needing to wrap one VC in another?
possibly, though i don't really have a sense of how we could concretely leverage that tool at the moment. i thought macros had to be additive, so i'm not sure we'd be able to change any existing code in client-supplied VCs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i thought macros had to be additive, so i'm not sure we'd be able to change any existing code in client-supplied VCs.
I dove a little deeper into Swift Macros after my initial comment and I think you're right, it looks like it may not be useful here.
@@ -22,7 +22,7 @@ import UIKit | |||
import Workflow | |||
|
|||
/// Drives view controllers from a root Workflow. | |||
public final class WorkflowHostingController<ScreenType, Output>: UIViewController where ScreenType: Screen { | |||
public final class WorkflowHostingController<ScreenType, Output>: WorkflowUIViewController where ScreenType: Screen { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kyleve responding here for threading purposes:
ViewControllerDescriptions not describing a ScreenViewController subclass does seem like a potential footgun here
Agreed. I don't think this is something we can/should enforce here, IMO.
could you clarify – you think this is a risk, but not one we should change the contract of ViewControllerDescription to deal with?
Motivation
we'd like to be able to build out some observation tooling that is aware of certain events that occur within WorkflowUI. specifically, view lifecycle data is of particular interest. currently the options for tying into UIViewController lifecycle events are either:
option
1.
may place a large burden on consumers that have many existing subclasses (of, say, ScreenViewController), and only works for theopen
types defined inWorkflowUI
, which excludesWorkflowHostingController
&DescribedViewController
. option2.
may require less code to implement, but adds indirection (swizzling is often not particularly discoverable), runtime overhead, and may not work for Swift-only methods.note: this is all currently implemented behind 'experimental' SPI declarations. the intent would be to treat this as sort of an 'alpha' version of this functionality, and to set expectations that it may have breaking changes going forward, without a major version increment in the library.
Implementation
to achieve this goal, we've implemented an approach that does the following:
UIViewController
subtypes defined inWorkflowUI
under a new parent classWorkflowUIViewController
Usage Example
the envisioned consumer is something like a shared telemetry system that wishes to observe certain WorkflowUI events and directly use or attach metadata to them. for example, imagine a consumer wished to log an event whenever a ViewController from WorkflowUI appeared on screen. with the proposed changes, this could be accomplished as follows:
Checklist